/** \file    vec2d.h
  * \brief   2D vector template definition.
  * \date    1998-2001
  * \author  Aloudin (Albert) Akhriev
  * \version $Header: /home/cscom1r4/cvsroot/cs/include/vec2d/vec2d.h,v 1.2 2004/12/23 17:42:00 cscom1r4 Exp $
  */

#ifndef _vec2d_h_
#define _vec2d_h_

#include <limits>
#include <cassert>
#include "cmmtype.h"
#include "cinline.h"
#include "math/round.h"
#include "decl.h"

namespace vec2d {

#define IS_INT(T)	(std::numeric_limits<T>::is_integer)
#define MIN_EPS(T)	(std::numeric_limits<T>::min()/std::numeric_limits<T>::epsilon())

//==========================================================================
// 2D vector.
// 'T' - coordinate type, 'H' - high type (e.g. T = short, H = int, T <= H).
//==========================================================================
template<class T, class H>
struct Vec2D : public Point2D<T>
{
  enum RelationsOfThreePoints
  {
    LEFT    = (1<<0), 
    RIGHT   = (1<<1), 
    BEYOND  = (1<<2),
    BEHIND  = (1<<3),
    BETWEEN = (1<<4),
    HEAD    = (1<<5),
    TAIL    = (1<<6)
  };


  Vec2D()
  { 
    x = (T)0;
    y = (T)0;
  }
 

  Vec2D(T nx, T ny)
  { 
    x = nx;
    y = ny;
  }


  template<class P>
  Vec2D(const Point2D<P> & v)
  { 
    if (IS_INT(T) && !IS_INT(P))
    {
      x = (T)(round(v.x));
      y = (T)(round(v.y));
    }
    else
    {
      x = (T)(v.x);
      y = (T)(v.y);
    }
  }


  int Size() const
  { 
    return 2;
  }


  Vec2D<T,H> & Set(T nx, T ny)
  { 
    x = nx;
    y = ny;
    return (*this);
  }


  H operator&(const Vec2D<T,H> & v) const        // inner product
  {
    return ((H)(x)*(H)(v.x) + (H)(y)*(H)(v.y));
  }


  H operator*(const Vec2D<T,H> & v) const        // outer product
  {
    return ((H)(x)*(H)(v.y) - (H)(y)*(H)(v.x));
  }


  Vec2D<T,H> & operator*=(T c)
  { 
    x = (T)(x*c);
    y = (T)(y*c);
    return (*this);
  }


  Vec2D<T,H> operator*(T c) const
  { 
    return Vec2D<T,H>((T)(x*c), (T)(y*c));
  }


  Vec2D<T,H> & operator+=(const Vec2D<T,H> & v)
  { 
    x = (T)(x+v.x);
    y = (T)(y+v.y);
    return (*this);
  }


  Vec2D<T,H> operator+(const Vec2D<T,H> & v) const
  { 
    return Vec2D<T,H>((T)(x+v.x), (T)(y+v.y));
  }


  Vec2D<T,H> & operator-=(const Vec2D<T,H> & v)
  {
    x = (T)(x-v.x);
    y = (T)(y-v.y);
    return (*this);
  }


  Vec2D<T,H> operator-(const Vec2D<T,H> & v) const
  {
    return Vec2D<T,H>((T)(x-v.x), (T)(y-v.y));
  }
 

  H SqLength() const
  { 
    return (sqr((H)(x)) + sqr((H)(y)));
  }


  double Length() const
  { 
    return sqrt(sqr((H)(x)) + sqr((H)(y)));
  }


  double SqDistance(const Vec2D<T,H> & v) const
  {
    H dx = x-v.x;
    H dy = y-v.y;
    return dx*dx + dy*dy;
  }

  double Distance(const Vec2D<T,H> & v) const
  {
    H dx = x-v.x;
    H dy = y-v.y;
    return sqrt(double(dx*dx + dy*dy));
  }


  double Angle(bool b0_2PI = false) const   // -PI...PI  or  0...2PI
  { 
    double a = atan2(y,x);
    if (b0_2PI && a < (T)0) a = fabs(a+(2.0*3.14159265358979323846));
    return a;
  }


  int ClassifyPosition(const Vec2D<T,H> & p0, const Vec2D<T,H> & p1) const
  { 
    Vec2D<T,H> a = p1 - p0;            // Classify relation of this point
    Vec2D<T,H> b = (*this) - p0;       // with respect to two given ones:
    H          ab = a*b;               // p0 *---------->* p1
                                       //    |
    if (ab > (H)0)                     //    * p2 = this
      return LEFT;
    if (ab < (H)0)
      return RIGHT;
    if (((H)(a.x)*(H)(b.x) < (H)0) || ((H)(a.y)*(H)(b.y) < (H)0))
      return BEHIND;
    if (a.SqLength() < b.SqLength())
      return BEYOND;
    if (p0 == (*this))
      return HEAD;
    if (p1 == (*this))
      return TAIL;
    return BETWEEN;
  }


  bool operator<(const Vec2D<T,H> & v) const
  { 
    return ((sqr((H)(x)) + sqr((H)(y))) < 
            (sqr((H)(v.x)) + sqr((H)(v.y))));
  }


  Vec2D<T,H> & Negate()
  { 
    x = -x;
    y = -y;
    return (*this);
  }


  Vec2D<T,H> operator-() const
  { 
    return Vec2D<T,H>((T)(-x), (T)(-y));
  }


  T & operator[](int n)
  { 
    assert(n == 0 || n == 1);
    return (n == 0) ? x : y;
  }


  T operator[](int n) const
  { 
    assert(n == 0 || n == 1);
    return (n == 0) ? x : y;
  }


  Vec2D<T,H> Perpendicular() const
  { 
    return Vec2D<T,H>((T)(-y), (T)(x));
  }


  bool HasIntegerCoord() const
  { 
    return IS_INT(T);
  }

  
  template<class P>
  Vec2D<T,H> & operator=(const Point2D<P> & v)
  { 
    if (IS_INT(T) && !IS_INT(P))
    {
      x = (T)(round(v.x));
      y = (T)(round(v.y));
    }
    else
    {
      x = (T)(v.x);
      y = (T)(v.y);
    }
    return (*this);
  }


  Vec2D<T,H> & operator/=(T c)
  { 
	  return Set((T)(x/c), (T)(y/c));
  }

  
  Vec2D<T,H> operator/(T c) const
  { 
      return Vec2D<T,H>((T)(x/c), (T)(y/c));
  }


  bool operator==(const Vec2D<T,H> & v) const
  {
    if (IS_INT(T))
      return (x == v.x && y == v.y);
    else
      return (fabs(x-v.x) <= MIN_EPS(T) &&
              fabs(y-v.y) <= MIN_EPS(T));
  }


  bool operator!=(const Vec2D<T,H> & v) const
  { 
    if (IS_INT(T))
      return (x != v.x || y != v.y);
    else
      return (fabs(x-v.x) > MIN_EPS(T) ||
              fabs(y-v.y) > MIN_EPS(T));
  }


  Vec2D<T,H> operator<<(int s) const
  { 
    assert(IS_INT(T));
    return Vec2D<T,H>((T)(x<<s), (T)(y<<s));
  }


  Vec2D<T,H> operator>>(int s) const
  {
    assert(IS_INT(T));
    return Vec2D<T,H>((T)(x>>s), (T)(y>>s));
  }


  int IsNeighbor(const Vec2D<T,H> & v) const
  {
    assert(IS_INT(T));
    return (x == v.x &&
            y == v.y) ? -1 : ((abs(x-v.x) <= 1 && 
                               abs(y-v.y) <= 1) ? +1 : 0);
  }


  double Normalize()
  {
    assert(!IS_INT(T));
    double len = sqrt(sqr((H)(x)) + sqr((H)(y)));
	if( len == 0 ) {
		x=y=0;
		return 0;
	} else {
		double rev = 1.0/len;
		x *= (T)rev;
		y *= (T)rev;
		return len;
	}
  }


  Vec2D<T,H> Rotated(double angle) const
  {
    assert(!IS_INT(T));
    double cs = cos(angle);
    double sn = sin(angle);
    return Vec2D<T,H>((T)(x*cs-y*sn), (T)(x*sn+y*cs));
  }

};

////////////////////////////////////////////////////////////////////////////
// Derived types.
////////////////////////////////////////////////////////////////////////////

// Point and Tangent
struct PntTan : public Vec2f {  Vec2f m_tan;  PntTan() : m_tan() {}  };

#undef IS_INT
#undef MIN_EPS

}

#endif //_vec2d_h_
